package com.yfbao.horizon.thread;

import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 异步函数调用
 * 可以将原来的同步方法快速转为异步方式调用，
 * todo:
 *  1、缺少异常处理
 *  2、缺少重置
 *  3、缺少后置处理
 *  4、缺少线程池设置
 *
 */
public class AsyncService {

    private static  final ExecutorService executorService = Executors.newFixedThreadPool(3);

    public void asyncDo(Object service, String methodName, Object[] args){
        if(!paramsCheck(service,methodName)){
            throw new RuntimeException("不支持的调用方式");
        }
        Method tarMethod = getTarMethod(service.getClass(), methodName, args);
        if(tarMethod ==null){
            throw new RuntimeException("没有找到反射调用的方法");
        }
        try {
            executorService.submit(new TaskRunnable(service,tarMethod,args));
        }catch (Throwable e){
            System.out.println(e);
        }
    }

    public  <T> Future<T> asyncDoFuture(Object service, String methodName, Object[] args){
        if(!paramsCheck(service,methodName)){
            throw new RuntimeException("不支持的调用方式");
        }
        Method tarMethod = getTarMethod(service.getClass(), methodName, args);
        if(tarMethod ==null){
            throw new RuntimeException("没有找到反射调用的方法");
        }
        TaskCallable<T> aclassTaskCallable = new TaskCallable<T>(service, tarMethod, args);
        try {
            Future<T> future = executorService.submit(aclassTaskCallable);
            return future;
        }catch (Throwable e){
            System.out.println(e);
        }
        return null;
    }

    private boolean paramsCheck(Object service, String method){
        if(service == null){
            System.out.println("异步反射调用驱动类不能为空");
            return false;
        }
        if(StringUtils.isBlank(method)){
            System.out.println("异步反射调用调用方法名不能为空");
            return false;
        }
        return true;
    }

    private  Method getTarMethod(Class<?> aClass,String methodName, Object[] args){
        try {
            if(args ==null || args.length==0 ){
                return aClass.getMethod(methodName);
            }
            Method[] methods = aClass.getMethods();
            List<Method> methodList = new ArrayList<Method>();
            for (Method method : methods) {
                if(method.getName().equals(methodName)){
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    if(parameterTypes==null || parameterTypes.length==0){
                        continue;
                    }
                    if(parameterTypes.length != args.length){
                        continue;
                    }
                    boolean isTarMethod = true;
                    for(int i=0;i<parameterTypes.length;i++){
                        Class<?> parameterType = parameterTypes[i];
                        if(!parameterType.isInstance(args[i])){
                            isTarMethod = false;
                            break;
                        }
                    }
                    if(isTarMethod){
                        methodList.add(method);
                    }
                }
            }
            if(methodList.isEmpty()){
                System.out.println("没找到映射方法");
                return null;
            }
            if(methodList.size()>1){
                System.out.println("不支持参数类型重载的方法调用,size:"+methodList.size());
                return null;
            }
            return methodList.get(0);
        }catch (Throwable t){
            System.out.println("方法映射异常");
            System.out.println(t);
        }
        return null;
    }

    class TaskRunnable implements Runnable {

        public TaskRunnable(Object service, Method method, Object[] args){
            this.service=service;
            this.method=method;
            this.args=args;
        }
        private Object service;
        private Method method;
        private Object[] args;

        public void run() {
            try{
                method.invoke(service, args);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    class TaskCallable<T> implements Callable<T> {

        public TaskCallable(Object service, Method method, Object[] args){
            this.service=service;
            this.method=method;
            this.args=args;
        }

        private Object service;
        private Method method;
        private Object[] args;

        public T call() throws Exception {
            try {
                T invoke =(T) method.invoke(service, args);
                return invoke;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
}
